#ifndef __QTCH_CONFIG_H__
#define __QTCH_CONFIG_H__
#include<string>
#include<algorithm>
#include<cctype>
#include<unordered_map>
#include<functional>
#include<memory>
#include<boost/lexical_cast.hpp>
#include<yaml-cpp/yaml.h>
#include<set>
#include<unordered_set>
#include<list>
#include<map>
#include<unordered_map>
#include <utility>




#include"log.h"



namespace qtch{

class ConfigVarBase{
public:
    typedef std::shared_ptr<ConfigVarBase> ptr;
    ConfigVarBase(std::string name,std::string describe="")
        :m_name(name)
        ,m_describe(describe){
        std::transform(m_name.begin(),m_name.end(),m_name.begin(),::tolower);
    }
    
    virtual ~ConfigVarBase(){}

    const std::string getName() const {return m_name;}

    const std::string getDescribe() const {return m_describe;}

    virtual std::string toString() = 0;

    virtual bool fromString(const std::string & val) =0;

    virtual std::string getTypeName() = 0;

protected:
    std::string m_name;         //配置名称
    std::string m_describe;     //配置描述
};

template<class T,class F>
class LexicalCast{
public:
    T operator()(F v){
        return boost::lexical_cast<T>(v);
    }
};

template<class T>
class LexicalCast<std::string ,std::vector<T>>{
public:
    std::string operator()(const std::vector<T>& v){
        YAML::Emitter out;
        out << YAML::BeginSeq;
        for(auto& it :v){
            out << LexicalCast<std::string, T>()(it);
        }
        out<< YAML::EndSeq;
        return out.c_str();
    }
};

template<class T>
class LexicalCast<std::vector<T>, std::string >{
public:
    std::vector<T> operator()(const std::string& v){
        std::vector<T> vec;
        YAML::Node node = YAML::Load(v);
        if(!node.IsSequence()){
            return vec;
        }
        for(size_t i = 0; i<node.size(); ++i){
            std::stringstream ss;
            ss << node[i];
            vec.push_back(LexicalCast<T,std::string>()(ss.str()));
        }
        return vec;
    }
};

template<class T>
class LexicalCast<std::string ,std::set<T>>{
public:
    std::string operator()(const std::set<T>& v){
        YAML::Emitter out;
        out << YAML::BeginSeq;
        for(auto& it :v){
            out << LexicalCast<std::string, T>()(it);
        }
        out<< YAML::EndSeq;
        return out.c_str();
    }
};

template<class T>
class LexicalCast<std::set<T>, std::string >{
public:
    std::set<T> operator()(const std::string& v){
        std::set<T> s;
        YAML::Node node = YAML::Load(v);
        if(!node.IsSequence()){
            return s;
        }
        for(size_t i = 0; i<node.size(); ++i){
            std::stringstream ss;
            ss<<node[i];
            s.insert(LexicalCast<T,std::string>()(ss.str()));
        }
        return s;
    }
};

template<class T>
class LexicalCast<std::string ,std::unordered_set<T>>{
public:
    std::string operator()(const std::unordered_set<T>& v){
        YAML::Emitter out;
        out << YAML::BeginSeq;
        for(auto& it :v){
            out << LexicalCast<std::string, T>()(it);
        }
        out<< YAML::EndSeq;
        return out.c_str();
    }
};

template<class T>
class LexicalCast<std::unordered_set<T>, std::string >{
public:
    std::unordered_set<T> operator()(const std::string& v){
        std::unordered_set<T> s;
        YAML::Node node = YAML::Load(v);
        if(!node.IsSequence()){
            return s;
        }
        for(size_t i = 0; i<node.size(); ++i){
            std::stringstream ss;
            ss << node[i];
            s.insert(LexicalCast<T,std::string>()(ss.str()));
        }
        return s;
    }
};

template<class T>
class LexicalCast<std::string ,std::list<T>>{
public:
    std::string operator()(const std::list<T>& v){
        YAML::Emitter out;
        out << YAML::BeginSeq;
        for(auto& it :v){
            out << LexicalCast<std::string, T>()(it);
        }
        out<< YAML::EndSeq;
        return out.c_str();
    }
};

template<class T>
class LexicalCast<std::list<T>, std::string >{
public:
    std::list<T> operator()(const std::string& v){
        std::list<T> l;
        YAML::Node node = YAML::Load(v);
        if(!node.IsSequence()){
            return l;
        }
        for(size_t i = 0; i<node.size(); ++i){
            std::stringstream ss;
            ss << node[i];
            l.push_back(LexicalCast<T,std::string>()(ss.str()));
        }
        return l;
    }
};

template<class T>
class LexicalCast<std::string ,std::map<std::string, T> >{
public:
    std::string operator()(const std::map<std::string, T>& v){
        YAML::Emitter out;
        out << YAML::BeginMap;
        for ( auto it =v.begin();it != v.end(); ++it){
            out << YAML::Key << it->first;
            out << YAML::Value << LexicalCast<std::string, T>()(it->second);

        }
        out<< YAML::EndMap;
        return out.c_str();
    }
};

template<class T>
class LexicalCast<std::map<std::string, T>, std::string >{
public:
    std::map<std::string, T> operator()(const std::string& v){
        std::map<std::string, T> m;
        YAML::Node node = YAML::Load(v);
        if(!node.IsMap()){
            return m;
        }
        for(YAML::iterator it = node.begin(); it != node.end(); ++it){
            std::stringstream ss;
            ss << it->second;
            m[it->first.as<std::string>()] = LexicalCast<T,std::string>()(ss.str());
            
        }
        return m;
    }
};

template<class T>
class LexicalCast<std::string ,std::unordered_map<std::string, T> >{
public:
    std::string operator()(const std::unordered_map<std::string, T>& v){
        YAML::Emitter out;
        out << YAML::BeginMap;
        for ( auto it =v.begin();it != v.end(); ++it){
            out << YAML::Key << it->first;
            out << YAML::Value << LexicalCast<std::string, T>()(it->second);

        }
        out<< YAML::EndMap;
        return out.c_str();
    }
};

template<class T>
class LexicalCast<std::unordered_map<std::string, T>, std::string >{
public:
    std::unordered_map<std::string, T> operator()(const std::string& v){
        std::unordered_map<std::string, T> m;
        YAML::Node node = YAML::Load(v);
        if(!node.IsMap()){
            return m;
        }
        for(YAML::iterator it = node.begin(); it != node.end(); ++it){
            std::stringstream ss;
            ss << it->second;
            m[it->first.as<std::string>()] = LexicalCast<T,std::string>()(ss.str());
        }
        return m;
    }
};



template<class T ,class fromSrc = LexicalCast<T,std::string>
                 ,class toSrc = LexicalCast<std::string,T> >
class ConfigVar :public ConfigVarBase{
public:
    typedef std::shared_ptr<ConfigVar> ptr;
    typedef std::function<void (const T& old_value,const T& new_value)> on_change_cb;
    typedef RWMutex MutexType;
    ConfigVar(const std::string& name,const T& default_value ,const std::string& describe="")
        :ConfigVarBase(name,describe)
        ,m_value(default_value){
    }

    uint64_t addListener(on_change_cb cb){
        static uint64_t s_fun_id =0;
        MutexType::WriteLock lock(m_mutex);
        ++s_fun_id;
        m_map[s_fun_id]=cb;
        return s_fun_id;
    }

    void delListener(uint64_t key){
        MutexType::WriteLock lock(m_mutex);
        m_map.erase(key);
    }
    
    void clearListener(){
        MutexType::WriteLock lock(m_mutex);
        m_map.clear();
    }

    const T getValue(){
        MutexType::ReadLock lock(m_mutex);
        return m_value;
    }

    void setValue(const T& v){
        MutexType::WriteLock lock(m_mutex);
        if(!(v == m_value)){
            for(auto it : m_map){
                it.second(m_value,v);
            }
            m_value = v;
        }
    }
    std::string toString() override{
        try{
            MutexType::ReadLock lock(m_mutex);
            return toSrc()(m_value);
        }
        catch(std::exception & e){
            QTCH_LOG_ERROR(QTCH_LOG_NAME("system"))<<"ConfigVar::toString exception"
                <<e.what() << " convert: " << TypeToName<T>() << " to string"
                <<" name=" <<m_name;
        }
        return "";
    }

    bool fromString(const std::string & val) override{
        try{
            setValue(fromSrc()(val));
            return true;
        }
        catch(std::exception & e){
            QTCH_LOG_ERROR(QTCH_LOG_NAME("system"))<<"ConfigVar::fromString exception"
                <<e.what() << " convert: string to " << TypeToName<T>()
                <<" name=" <<m_name
                <<" - "<<val;
        }
        return false;
    }

    std::string getTypeName() override{
        MutexType::ReadLock lock(m_mutex);
        return TypeToName<T>();
    }
    

protected:
    T m_value;
    std::unordered_map<uint64_t,on_change_cb> m_map;
    MutexType m_mutex;
};

class Config{
public:
    typedef std::unordered_map<std::string, ConfigVarBase::ptr> configVarMap;

    typedef RWMutex MutexType;
    
    template<class T>
    static typename ConfigVar<T>::ptr LookUp(const std::string & name,
                                    const T& default_value,
                                    const std::string &describe = "")  {
        {
            MutexType::ReadLock lock(getMutex());
            auto it = GetDatas().find(name);
            if(it != GetDatas().end()){
                auto temp = std::dynamic_pointer_cast<ConfigVar<T> >(it->second);
                if(temp){
                    QTCH_LOG_DEBUG(QTCH_LOG_NAME("system"))<<"LookUp "<<name<<" exists";
                    return temp;
                }
                else{
                    QTCH_LOG_ERROR(QTCH_LOG_NAME("system"))<<"LookUp "<<name<<" exists but type not right";
                    return nullptr;
                }
            }
        }
        

        if(name.find_first_not_of("abcdefghijklmnopqrstuvwxyz0123456789_.")!=std::string::npos){
            QTCH_LOG_ERROR(QTCH_LOG_NAME("system"))<<"LookUp name is invalid "<< name;
            return nullptr;
        }

        typename ConfigVar<T>::ptr v(new ConfigVar<T>(name,default_value, describe));
        MutexType::ReadLock lock(getMutex());
        GetDatas()[name] = v;
        return v;
    }

    template<class T>
    static typename ConfigVar<T>::ptr LookUp(const std::string name){
        auto it = GetDatas().find(name);
        if(it != GetDatas().end()){
            return std::dynamic_pointer_cast<ConfigVar<T> >(it->second);
        }
        return nullptr;
    }

    static ConfigVarBase::ptr LookUpBase(const std::string name);
    static void LoadConfigDir(const std::string path);
    static void LoadYamlFile(const std::string filePath);
    static void ListAllMember(std::string prefix,YAML::Node node,std::list<std::pair<std::string, YAML::Node> >& save);

private:


    static configVarMap& GetDatas(){
        static configVarMap s_datas;
        return s_datas;
    }

    static MutexType& getMutex(){
        static MutexType s_mutex;
        return s_mutex;
    }
};


}

#define QTCH_CONFIG_VALUE_ADD_LISTENER_STRING(configVal,val) \
    val = configVal->getValue();\
    configVal->addListener([](const std::string& _old_value,const std::string& _new_value){\
        val = _new_value;\
    });

#define QTCH_CONFIG_VALUE_ADD_LISTENER_UINT64(configVal,val) \
    val = configVal->getValue();\
    configVal->addListener([](const uint64_t& _old_value,const uint64_t& _new_value){\
        val = _new_value;\
    });


#endif